#!/usr/bin/env bash

include "objectv5.sh"
include "output.sh"
include "scope.sh"
include "test.sh"

ObjectV5::DefineClass FileLocker "
    --cargs=file
    --string=file
"

function FileLocker::FileLocker()
{
    Trace::StartTrace "FileLocker"
    [ $# == 0 ] && assert_fail "this"
    local this="$1"; shift
    local file="$(FileLocker::file "$this").lock"
    local pidfile="$file.pidlock"
    FileLocker::file "$this" "$file"

    while true; do
        if (set -C; echo $$ >"$file") 2>/dev/null; then
            debuglog "locked $file"
            break
        else
            debuglog "checking $file"
            if (set -C; echo $$ >"$pidfile") 2>/dev/null; then
                local pid="$(cat "$file" 2>/dev/null)"
                if ! [ -e "/proc/$pid" ]; then
                    debuglog "remove $file"
                    rm -f "$file"
                fi
                debuglog "checked $file"
                rm -f "$pidfile"
            fi
        fi
    done
    return
}

function FileLocker::~FileLocker()
{
    [ $# == 0 ] && assert_fail "this"
    local this="$1"; shift
    local file="$(FileLocker::file "$this")"
    debuglog "unlocked $file"
    rm -f "$file"
    Trace::EndTrace
    return
}

function FileLocker::Test1()
{
    local testfile="$(mktemp --dry-run "/tmp/file_locker_test.XXXXXXXX")"
    echo 0 > "$testfile"

    function FileLocker::Test1::LongTask()
    {
        local i
        for (( i = 0; i < 100; i++ )); do
            FileLocker::New locker{"$testfile"}
            local val="$(( $(cat "$testfile") + 1 ))"
            debuglog "inc val: $val"
            echo "$val" > "$testfile"
            FileLocker::Del locker
        done
        return
    }

    local pids=()
    FileLocker::Test1::LongTask &
    pids+=($!)
    FileLocker::Test1::LongTask &
    pids+=($!)
    FileLocker::Test1::LongTask &
    pids+=($!)
    FileLocker::Test1::LongTask &
    pids+=($!)

    wait "${pids[@]}"
    test::run_cmd cat "$testfile"
    test::should_be 400
    rm "$testfile"
    return
}
test::register_test FileLocker::Test1

function FileLocker::Test2()
{
    local testfile="$(mktemp --dry-run "/tmp/file_locker_test.XXXXXXXX")"
    echo 0 > "$testfile"

    function FileLocker::Test2::LongTask()
    {
        local i
        for (( i = 0; i < 100; i++ )); do
            FileLocker::New locker{"$testfile"}
            local val="$(( $(cat "$testfile") + 1 ))"
            debuglog "inc val: $val"
            echo "$val" > "$testfile"
            FileLocker::Del locker
        done
        return
    }

    FileLocker::Test1::LongTask
    FileLocker::Test1::LongTask
    FileLocker::Test1::LongTask
    FileLocker::Test1::LongTask

    test::run_cmd cat "$testfile"
    test::should_be 400
    rm "$testfile"
    return
}
test::register_test FileLocker::Test2
